// Filename: uart.rs

use crate::common::tools::{delay, get32, put32};

// Constants for register addresses
const GPFSEL1: u64 = 0x3F200004;
const GPPUD: u64 = 0x3F200094;
const GPPUDCLK0: u64 = 0x3F200098;
const AUX_ENABLES: u64 = 0x3F215004;
const AUX_MU_IO_REG: u64 = 0x3F215040;
const AUX_MU_IER_REG: u64 = 0x3F215044;
const AUX_MU_IIR_REG: u64 = 0x3F215048;
const AUX_MU_LCR_REG: u64 = 0x3F21504C;
const AUX_MU_MCR_REG: u64 = 0x3F215050;
const AUX_MU_LSR_REG: u64 = 0x3F215054;
const AUX_MU_CNTL_REG: u64 = 0x3F215060;
const AUX_MU_BAUD_REG: u64 = 0x3F215068;

// Constants for UART operations
const UART_RX: u32 = 0x01;
const UART_TX: u32 = 0x20;

// Constants for non-blocking UART receive
const NB_UART_NRET: u32 = 0xFFFFFFFF;

pub fn uart_init() {
    unsafe {
        let mut ra = get32(GPFSEL1);
        ra &= !(7 << 12);
        ra |= 2 << 12;
        ra &= !(7 << 15);
        ra |= 2 << 15;
        put32(GPFSEL1, ra);

        put32(GPPUD, 0);
        delay(150);
        put32(GPPUDCLK0, (1 << 14) | (1 << 15));
        delay(150);
        put32(GPPUDCLK0, 0);

        put32(AUX_ENABLES, 1);
        put32(AUX_MU_IER_REG, 0);
        put32(AUX_MU_CNTL_REG, 0);
        put32(AUX_MU_IER_REG, 0);
        put32(AUX_MU_LCR_REG, 3);
        put32(AUX_MU_MCR_REG, 0);
        put32(AUX_MU_BAUD_REG, 270);

        put32(AUX_MU_CNTL_REG, 3);

        // Clear the screen
        uart_send(12);
        uart_send(27);
        uart_send(b'['.into());
        uart_send(b'2'.into());
        uart_send(b'J'.into());
    }
}

pub fn uart_lsr() -> u32 {
    unsafe { get32(AUX_MU_LSR_REG) }
}

pub fn uart_recv() -> u32 {
    unsafe {
        while uart_lsr() & UART_RX == 0 {}

        get32(AUX_MU_IO_REG) & 0xFF
    }
}

pub fn nb_uart_recv() -> u32 {
    unsafe {
        if uart_lsr() & UART_RX != 0 {
            get32(AUX_MU_IO_REG) & 0xFF
        } else {
            NB_UART_NRET
        }
    }
}

pub fn uart_send(c: u32) {
    unsafe {
        while uart_lsr() & UART_TX == 0 {}

        put32(AUX_MU_IO_REG, c);
    }
}
